/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 */

import path from 'path';

import { DurationEvent, FileSet, MetricLogType } from '@ohos/hvigor';
import fse from 'fs-extra';

import { NodeCommandBuilder } from '../../builder/node-command-builder.js';
import { ModulePathInfoIml } from '../../common/iml/module-path-info-iml.js';
import { BuildArtifactConst } from '../../const/build-directory-const.js';
import { CommonConst } from '../../const/common-const.js';
import { CodeType } from '../../enum/code-type-enum.js';
import { LegacyAbilityModel } from '../../model/ability/legacy-ability-model.js';
import { LegacyModuleModelImpl } from '../../model/module/legacy-module-model-impl.js';
import { FileUtil } from '../../utils/file-util.js';
import { OhosLogger } from '../../utils/log/ohos-logger.js';
import { ProcessUtils } from '../../utils/process-utils.js';
import { TaskNames } from '../common/task-names.js';
import { CompileNode } from '../compile-node.js';
import { ModuleTargetData } from '../data/hap-task-target-data.js';
import { TargetTaskService } from '../service/target-task-service.js';
import { LegacyHookCompileResource } from './hook/legacy-hook-compile-resource.js';
import { LegacyGenerateJsManifest } from './legacy-generate-js-manifest.js';
import { LegacyGenerateLoaderJson } from './legacy-generate-loader-json.js';

/**
 * 调用ace-loader编译老模型的js的瘦设备代码
 *
 * @since 2022/5/17
 */
export class LegacyCompileLiteNode extends CompileNode {
  private _logger: OhosLogger = OhosLogger.getLogger(LegacyCompileLiteNode.name);

  constructor(taskService: TargetTaskService) {
    super(taskService, CodeType.JS, TaskNames.LegacyFATask.COMPILE_LITE_NODE);
  }

  declareOutputFiles(): FileSet {
    const aceModuleBuild = path.resolve(this.pathInfo.getInterMediatesLoaderOutLitePath(), this.codeType);
    return new FileSet().addEntry(aceModuleBuild, { isDirectory: true });
  }

  taskShouldDo(): boolean {
    return this.service.hasLiteDevice();
  }

  initTaskDepends() {
    this.declareDepends(LegacyGenerateJsManifest.name);
    this.declareDepends(LegacyHookCompileResource.name);
    this.declareDepends(LegacyGenerateLoaderJson.name);
  }

  protected async doTaskAction(): Promise<void> {
    const moduleModel: LegacyModuleModelImpl = this.service.getModuleModel()! as LegacyModuleModelImpl;
    const codeModel = moduleModel.getSourceSetByTargetName(this.targetName).getCodeMap().get(this.codeType);
    if (codeModel === undefined) {
      this._logger.printErrorExit('LITE_WEARABLE_ONLY_SUPPORT_JS', [this.pathInfo]);
      return;
    }

    const subTaskName = 'generate compile-JS-component command';
    const subDurationEvent = this.durationEvent.createSubEvent(subTaskName, '');
    subDurationEvent.start();

    const aceModuleRoot: string = codeModel.getSrcPath();
    const pathInfo: ModulePathInfoIml = this.pathInfo;

    // 依赖manifest.json的生成
    const aceManifestPath: string = path.resolve(pathInfo.getIntermediatesLegacyManifestJson());
    const aceModuleBuild: string = path.resolve(this.pathInfo.getInterMediatesLoaderOutLitePath(), CodeType.JS);
    const abilityObjs: LegacyAbilityModel[] = moduleModel.getLegacyAbilities(this.targetName);
    this.checkLocalDependency(abilityObjs, this._logger);

    FileUtil.checkDirWithoutDelete(pathInfo.getIntermediatesLiteBinSource());
    fse.copySync(moduleModel.getSourceSetByTargetName().getLegacyModuleTargetRes().getResourcePath(), pathInfo.getIntermediatesLiteBinSource(), {
      filter: (src) => {
        return fse.statSync(src).isDirectory() || src.toLowerCase().endsWith('.png') || src.toLowerCase().endsWith('.jpg');
      },
      recursive: true,
    });
    for (let i = 0; i < abilityObjs.length; i++) {
      const abilityInfo: LegacyAbilityModel = abilityObjs[i];
      const cmdEnv: NodeJS.ProcessEnv = {
        ...this.commonOption,
        abilityType: abilityInfo.getType(),
        aceManifestPath: path.resolve(aceManifestPath, abilityInfo.getRelateSrcPath(), BuildArtifactConst.LEGACY_MANIFEST_JSON),
        aceModuleRoot: path.resolve(aceModuleRoot, abilityInfo.getRelateSrcPath()),
        aceModuleBuild: path.resolve(aceModuleBuild, abilityInfo.getRelateSrcPath()),
        hapMode: (!this.targetService.isDebug()).toString(),
        img2bin: 'true',
        iconPath: pathInfo.getIntermediatesLiteBinSource(),
        cachePath: this.getTaskTempDir(this.targetData),
        aceBuildJson: path.resolve(pathInfo.getIntermediatesLoaderPath(), abilityInfo.getRelateSrcPath(), BuildArtifactConst.LOADER_JSON),
      };

      await this.doRealLoaderCompile(
        cmdEnv,
        (targetData: ModuleTargetData, abilityPath: string) => {
          this.moveReleaseMap(targetData, this._logger, abilityPath);
        },
        [this.targetData, `js/${abilityInfo.getRelateSrcPath()}`],
        subDurationEvent,
        [0, 1],
      );
    }
  }

  protected moveReleaseMap(targetData: ModuleTargetData, log: OhosLogger, abilityPath: string = this.codeType): void {
    const releaseMapDir: string = path.resolve(targetData.getPathInfo().getInterMediatesLoaderOutLitePath(), abilityPath, BuildArtifactConst.RELEASE_MAP);

    if (!fse.existsSync(releaseMapDir)) {
      return;
    }
    const tempDir: string = path.resolve(this.getTaskTempDir(targetData), abilityPath, BuildArtifactConst.RELEASE_MAP);
    if (fse.existsSync(tempDir)) {
      fse.removeSync(tempDir);
    }
    fse.moveSync(releaseMapDir, tempDir);
    log.debug(`move ${releaseMapDir} to ${tempDir}`);
  }

  protected async doRealLoaderCompile(
    cmdEnv: NodeJS.ProcessEnv,
    callback: Function,
    callbackInput: unknown[],
    subDurationEvent: DurationEvent,
    targetWorkers?: number[],
  ): Promise<void> {
    const nodeCommandBuilder: NodeCommandBuilder = new NodeCommandBuilder(true)
      .addWebpackPath('./node_modules/webpack/bin/webpack.js')
      .addWebpackConfig(CommonConst.ACE_LITE_WEBPACK_FILE)
      .addBuildMode(this.targetService.isDebug());

    this._logger._printDebugCommand('NodeEnv', cmdEnv);
    this._logger._printDebugCommand('lite-loader', nodeCommandBuilder.build());

    const inputData = this.getInputData(cmdEnv, nodeCommandBuilder.build());

    subDurationEvent.stop();
    subDurationEvent.setLog(subDurationEvent.getName(), MetricLogType.INFO);
    const submitTaskName = 'submit compile-JS-component task to work pool';
    const subToolDurationEvent = this.durationEvent.createSubEvent(submitTaskName, '');
    subToolDurationEvent.start();

    await new ProcessUtils(inputData.moduleName, inputData.taskName).submitExecutionWithoutReturn(
      this,
      this.getWorkerPool(),
      inputData,
      callback,
      callbackInput,
      subDurationEvent,
      targetWorkers,
    );

    subToolDurationEvent.stop();
    subToolDurationEvent.setLog(submitTaskName, MetricLogType.INFO);
  }
}
